//
//  SFCLifeCycleTests.m
//  SVNForCocoa
//
//  Created by Jeremy Pereira on 11/11/2013.
//  Copyright (c) 2013 Jeremy Pereira. All rights reserved.
//

/*!
 *    @brief Tests for notrmal SVN lifecycle.
 */

#import <XCTest/XCTest.h>
#import "SFCFileSetUpHelper.h"
#import "SFCClient.h"
#import "SFCRevision.h"
#import "SFCTestAuthProvider.h"
#import "SFCLib+Internals.h"

static NSString* const PATCH_FILE = @"test.patch";

/*!
 *    @brief Tests for the normal svn lifecycle operations.
 *
 *    Tests are done against a repository containing a couple of files and a
 *    working copy that has been checked out and is up to date.
 */
@interface SFCLifeCycleTests : XCTestCase

-(void) createPatchFile;

@end

@implementation SFCLifeCycleTests
{
	SFCFileSetUpHelper* fileHelper;
    SFCClient* client;
}

/*!
 *    @brief Sets up the initial test files.
 *
 *    The repo has a single file in it with a couple of revisions.  The 
 *    working copy is a fully checked out and up to date working copy.
 */
- (void)setUp
{
    [super setUp];
    NSString* cwd = [[NSFileManager defaultManager] currentDirectoryPath];
    if ([cwd isEqualToString: @"/"])
    {
        [[NSFileManager defaultManager] changeCurrentDirectoryPath: NSTemporaryDirectory()];
    }
    if (fileHelper == nil)
    {
        fileHelper = [[SFCFileSetUpHelper alloc] initWithFolder: [self className]];
    }
    cwd = [[NSFileManager defaultManager] currentDirectoryPath];
    NSLog(@"CWD = %@", cwd);
    [fileHelper resetFiles];
    [fileHelper addFileToRepository: @"test.txt" content: @"line 1\n"];
    [fileHelper updateFileInRepository: @"test.txt" appendContent: @"line 2\n"];
    [fileHelper checkoutWc];
    NSError* error;
    client = [[SFCClient alloc] initWithError: &error];
    if (client == nil)
    {
        @throw [NSException exceptionWithName: @"SFCLifeCycleTests"
                                       reason: [NSString stringWithFormat: @"%@", error]
                                     userInfo: nil];
    }
}

- (void)tearDown
{
    // Put teardown code here. This method is called after the invocation of
    // each test method in the class.
    [super tearDown];
}

-(void) createPatchFile
{
    NSString* patchFile = @""
    "Index: test.txt\n"
    "===================================================================\n"
    "--- test.txt	(revision 2)\n"
    "+++ test.txt	(working copy)\n"
    "@@ -1,2 +1,3 @@\n"
    " line 1\n"
    "+line 2.5\n"
    " line 2\n";

    NSData* patchData = [patchFile dataUsingEncoding: NSUTF8StringEncoding];
    [patchData writeToFile: PATCH_FILE atomically: NO];
}


#define UNIMPLEMENTED_TEST(S)							\
-(void) S												\
{														\
	XCTFail(@"%s not implemented", __PRETTY_FUNCTION__);\
}

/*!
 *    @brief Test a commit operation.
 */
-(void) testCommit
{
    NSString* logMessage = [NSString stringWithFormat: @"log message @ %@", [NSDate date]];
	[fileHelper append: @"line3\n" toFileInWc: @"test.txt"];
	NSError* error = nil;
    [client setCommitLogBlock: ^bool(NSArray* commitItems,
                                     NSString* __autoreleasing* logMessageRet,
                                     NSString* __autoreleasing* tempFilePathRet)
    {
		NSLog(@"Commit items: %@", commitItems);
        *logMessageRet = logMessage;
        return true;
    }];

    __block bool committedBlockCalled = false;
    bool result = [client commit: @[[fileHelper wcPath]]
                           depth: SFC_DEPTH_INFINITY
           				 options: 0
                      changLists: nil
      				revPropTable: nil
                  committedBlock: ^bool(SFCCommitInfo *commitInfo)
    							  {
                                      NSLog(@"%@", commitInfo);
                                      committedBlockCalled = true;
                                      return true;
                                  }
                           error: &error];
	if (!result)
    {
        XCTFail(@"commit failed %@", error);
    }
    else
    {
		XCTAssertTrue(committedBlockCalled, @"The commit block wasn't called.");
        NSString* logOutput = [fileHelper outputOfTask: SVN_CMD
                                         withArguments: @[ @"log",
                                                           [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"]]];
		NSRange logMessageRange = [logOutput rangeOfString: logMessage];
        XCTAssertTrue(logMessageRange.location != NSNotFound,
                      @"Log message missing from %@",
                      logOutput);
    }
}

/*!
 *    @brief Test a log operation.
 */
-(void) testLog
{
    NSError* error = nil;
    __block int count = 0;
	bool result = [client log: @[[fileHelper wcPath]]
                  pegRevision: nil
               revisionRanges: @[[SFCRevisionRange rangeFrom: [SFCRevision number: 0] to: [SFCRevision head]]]
                        limit: 0
                      options: SFC_LOG_OPTION_DISCOVER_CHANGED_PATHS
                 revPropTable: nil
                     logBlock: ^bool (SFCLogEntry *logInfo)
    						   {
                                   NSLog(@"%@", logInfo);
                                   count++;
                                   return true;
                               }
                        error: &error];
    if (!result)
    {
        XCTFail(@"Log failed %@", error);
    }
    else
    {
        XCTAssertTrue(count == 3, @"Wrong numbwer of revisions found %d", count);
    }
}

-(void) testStatus
{
    NSError* error = nil;
    __block int count = 0;
	bool result = [client status: [fileHelper wcPath]
                        revision: nil
                           depth: SFC_DEPTH_INFINITY
                         options: SFC_STATUS_OPTION_GET_ALL
                     changeLists: nil
                     statusBlock: ^bool (NSString *path, SFCStatusEntry *statusInfo)
                        {
                            NSLog(@"%@ %@", path, statusInfo);
                            count++;
                            return true;
                        }
                  resultRevision: NULL
                           error: &error];
    if (!result)
    {
        XCTFail(@"Status failed %@", error);
    }
    else
    {
        XCTAssertTrue(count == 2, @"Wrong number of entries found %d", count);
    }
}

-(void) testUpdate
{
	[fileHelper addFileToRepository: @"another.txt" content: @"line 1\n"];
    NSArray* resultRevisions = nil;
    NSError* error = nil;
    [client setNotifyBlock: ^(SFCWCNotification* notification)
    {
        NSLog(@"notification %@", notification);
    }];
    bool result = [client update: @[[fileHelper wcPath]]
          				revision: nil
                           depth: SFC_DEPTH_INFINITY
           				 options: 0
                 resultRevisions: &resultRevisions
                           error: &error];
    if (!result)
    {
        XCTFail(@"Update failed %@", error);
    }
    else
    {
        XCTAssertTrue([resultRevisions count] == 1 && [[resultRevisions objectAtIndex: 0] longValue] == 3,
                      @"Wrong revisions found %@",
                      resultRevisions);
    }
}

static NSString* const FILE_TO_ADD = @"addedfile";

/*!
 *    @brief Test adding a new file to the working copy.
 */
-(void) testAdd
{
    NSString* fullAddPath = [[fileHelper wcPath] stringByAppendingPathComponent: FILE_TO_ADD];
    [[@"add content\n" dataUsingEncoding: NSUTF8StringEncoding]writeToFile: fullAddPath
                                                                atomically: NO];

	NSError* error = nil;
    bool result = [client add: fullAddPath
                        depth: SFC_DEPTH_INFINITY
                      options: 0
                        error: &error];
    if (!result)
    {
        XCTFail(@"Update failed %@", error);
    }
    else
    {
        XCTAssertTrue([fileHelper file: fullAddPath statusIs: @"A"],
                      @"File %@ is not added",
                      fullAddPath);
    }
}
/*!
 *    @brief Test reverting a file in the working copy.
 */
-(void) testRevert
{
	NSString* fullTestPath = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
	size_t testFileSize = [fileHelper sizeOfFile: fullTestPath];
	[fileHelper append: @"line3\n" toFileInWc: @"test.txt"];
	if (![fileHelper file: fullTestPath statusIs: @"M"])
    {
        @throw [NSException exceptionWithName: [self className]
                                       reason: [NSString stringWithFormat: @"%s failed to modify %@", __PRETTY_FUNCTION__, fullTestPath]
                                     userInfo: nil];
    }
    NSError* error = nil;
    bool result = [client revert: @[[fileHelper wcPath]]
                           depth: SFC_DEPTH_INFINITY
                     changeLists: nil
                           error: &error];
    if (!result)
    {
        XCTFail(@"Revert failed %@", error);
    }
    else
    {
        XCTAssertTrue([fileHelper file: fullTestPath statusIs: @" "],
                      @"Status not reverted");
        XCTAssertTrue(testFileSize == [fileHelper sizeOfFile: fullTestPath],
                      @"File did not revert to old content");
    }
}

-(void) testInfo
{
    __block bool infoBlockInvoked = false;

    NSError* error = nil;
    bool result = [client infoPath: [fileHelper wcPath]
                       pegRevision: nil
                          revision: nil
                             depth: SFC_DEPTH_INFINITY
                     fetchExcluded: false
                   fetchActualOnly: false
                       changeLists: nil
                         infoBlock:^bool(NSString *path, SFCClientInfo *info, NSError *__autoreleasing *error)
    					 {
                             NSLog(@"%@", info);
                             infoBlockInvoked = true;
                             return true;
                         }
                             error: &error];
    if (!result)
    {
        XCTFail(@"Info falied: %@", error);
    }
    else
    {
        XCTAssertTrue(infoBlockInvoked, @"Block was not invoked");
    }
}

NSString* const TEST_DIR = @"testdir";

-(void) testMkDir
{
    bool mkDirOK = false;
    NSError* error = nil;
    NSString* testDir = [[fileHelper wcPath] stringByAppendingPathComponent: TEST_DIR];
	mkDirOK = [client mkDir: @[ testDir ]
                makeParents: true
         revisionProperties: nil
             committedBlock: nil
                      error: &error];
    XCTAssertTrue(mkDirOK, @"Error making directory %@", error);
	XCTAssertTrue([fileHelper file: testDir statusIs: @"A"], @"%@ is not added", testDir);
}

-(void) testLockAndUnlock
{
    NSArray* lockArray = @[[[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"]];
    SFCTestUserNameProvider* userNameProvider = [[SFCTestUserNameProvider alloc] init];
    [client addAuthenticationProvider: userNameProvider];

    NSString* testFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
	NSError* error = nil;
	bool result = [client lock: lockArray
                       comment: @"Locking test"
                     stealLock: false
                         error: &error];
    if (!result)
    {
        XCTFail(@"Lock falied: %@", error);
    }
    else
    {
        XCTAssertTrue([fileHelper file: testFile
                              statusIs: @"     K"], @"%@ is not locked", testFile);
        result = [client unlock: lockArray
                      breakLock: false
                          error: &error];
        if (!result)
        {
            XCTFail(@"unlock falied: %@", error);
        }
		else
        {
            XCTAssertFalse([fileHelper file: testFile
                                   statusIs: @"     K"], @"%@ is still locked", testFile);
        }
    }
}

-(void) testDiff
{
	[fileHelper append: @"line3\n" toFileInWc: @"test.txt"];
    NSString* diffFileName = [[fileHelper rootPath] stringByAppendingPathComponent: @"diff.out"];
    //    [fileHelper touch: diffFileName];
	NSOutputStream* out = [NSOutputStream outputStreamToFileAtPath: diffFileName
                                                            append: NO];
    [out open];
    NSString* fileToDiff = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    NSError* error = nil;
    bool result = [client diffCmdOptions: @[]
                                   path1: fileToDiff
                               revision1: [SFCRevision base]
                         		   path2: fileToDiff
                     		   revision2: [SFCRevision working]
                           relativeToDir: nil
                         		   depth: SFC_DEPTH_INFINITY
                       		     options: 0
                		  headerEncoding: NSUTF8StringEncoding
                               outStream: out
                    		 errorStream: nil
                   			 changeLists: nil
                                   error: &error];
    [out close];
    if (!result)
    {
        XCTFail(@"diff failed: %@", error);
    }

}

-(void) testAddToChangeList
{
    NSString* testFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    NSError* error = nil;
    bool result = [client addPaths: @[testFile]
                      toChangeList: @"myChangeList"
                             depth: SFC_DEPTH_INFINITY
                       changeLists: nil
                             error: &error];
    XCTAssertTrue(result, @"Add to change list failed, reason %@", error);
    NSString* statusOut = [fileHelper outputOfTask: SVN_CMD
                                     withArguments: @[ @"status", [fileHelper wcPath]]];
    NSRange rangeOfChangeListStart = [statusOut rangeOfString: @"--- Changelist 'myChangeList':"];
    XCTAssertTrue(rangeOfChangeListStart.location != NSNotFound, @"The changelist is not in the status output");
}

-(void) testPropGet
{
    NSString* testFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    NSString* newDir = [[fileHelper wcPath] stringByAppendingPathComponent: @"directory"];
    newDir = [newDir absolutePath];

    [fileHelper runTask: SVN_CMD
          withArguments: @[ @"propset", @"svn:global-ignores", @"ignorethis", [fileHelper wcPath]]];
    [fileHelper runTask: SVN_CMD
          withArguments: @[ @"propset", @"myProperty", @"jeremy", testFile ]];
    [fileHelper runTask: SVN_CMD withArguments: @[@"mkdir", newDir]];
    [fileHelper runTask: SVN_CMD
          withArguments: @[ @"propset", @"myProperty", @"foo", newDir ]];

    NSDictionary* properties = nil;
    NSArray* inheritedItems = nil;
    SFCRevNum actualRevision;
    NSError* error = nil;

    bool result = [client propGet: @"myProperty"
                           target: newDir
                      pegRevision: nil
                         revision: [SFCRevision unspecified]
                            depth: SFC_DEPTH_INFINITY
                      changeLists: nil
                       properties: &properties
                        inherited: &inheritedItems
                   actualRevision: &actualRevision
                            error: &error];

    if (!result)
    {
        XCTFail(@"prop get failed, reason %@", error);
    }
    else
    {
        XCTAssertTrue([[properties objectForKey: newDir] isEqualToString: @"foo"],
                      @"Properties for %@ do not contain the expected property, %@",
                      newDir,
                      properties);
        /*
         *  Now test inherited properties
         */
        result = [client propGet: @"svn:global-ignores"
                          target: newDir
                     pegRevision: nil
                        revision: [SFCRevision unspecified]
                           depth: SFC_DEPTH_INFINITY
                     changeLists: nil
                      properties: &properties
                       inherited: &inheritedItems
                  actualRevision: &actualRevision
                           error: &error];

        if (!result)
        {
            XCTFail(@"prop get failed, reason %@", error);
        }
        else
        {
            XCTAssertTrue([inheritedItems count] == 1, @"Wrong number of inherited properties");
            // TODO: This test is failing on the server build.  Fix it.
//            XCTAssertTrue([[[inheritedItems objectAtIndex: 0] pathOrURL] isEqualToString: [[fileHelper wcPath] absolutePath]],
//                          @"Inherited items for %@ do not contain the expected property, %@",
//                          newDir,
//                          inheritedItems);
        }
    }
}

-(void) testPropSetLocal
{
    NSString* testFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    NSError* error = nil;
    bool result = [client propSet: @"testProperty"
                            value: @"foo"
                        wcTargets: @[ testFile ]
                            depth: SFC_DEPTH_INFINITY
                       skipChecks: false
                      changeLists: nil
                            error: &error];
    XCTAssertTrue(result, @"Failed to set the property, %@", error);
    if (result)
    {
        NSString* output = [fileHelper outputOfTask: SVN_CMD
                                      withArguments: @[ @"propget", @"testProperty", testFile ]];
        NSRange rangeOfFoo = [output rangeOfString: @"foo"];
        XCTAssertTrue(output != nil && rangeOfFoo.location != NSNotFound, @"Property not set in %@", output);
    }
}

-(void) testPropSetRemote
{
    SFCTestUserNameProvider* userNameProvider = [[SFCTestUserNameProvider alloc] init];
    [client addAuthenticationProvider: userNameProvider];
    NSString* testFile = [[[fileHelper repoPath] stringByAppendingPathComponent: @"test.txt"] absolutePath];
	NSURL* testUrl = [NSURL fileURLWithPath: testFile];
    NSError* error = nil;
    NSString* logMessage = @"Committed property";
    [client setCommitLogBlock: ^bool(NSArray* commitItems,
                                     NSString* __autoreleasing* logMessageRet,
                                     NSString* __autoreleasing* tempFilePathRet)
     {
         NSLog(@"Commit items: %@", commitItems);
         *logMessageRet = logMessage;
         return true;
     }];
	__block bool committedBlockCalled = false;
    bool result = [client propSet: @"testProperty"
                            value: @"foo"
                       repoTarget: testUrl
                            depth: SFC_DEPTH_INFINITY
                       skipChecks: false
                         revision: 2
               revisionProperties: nil
                   committedBlock: ^bool(SFCCommitInfo *commitInfo)
                   {
                       NSLog(@"%@", commitInfo);
                       committedBlockCalled = true;
                       return true;
                   }
                            error: &error];

    XCTAssertTrue(result, @"prop set failed, reason %@", error);
    if (result)
    {
        NSString* wcTestFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
        [fileHelper runTask: SVN_CMD withArguments: @[@"update", [fileHelper wcPath]]];
        NSString* output = [fileHelper outputOfTask: SVN_CMD
                                      withArguments: @[ @"propget", @"testProperty", wcTestFile ]];
        NSRange rangeOfFoo = [output rangeOfString: @"foo"];
        XCTAssertTrue(output != nil && rangeOfFoo.location != NSNotFound, @"Property not set in %@", output);
    }
}

-(void) testCat
{
    NSString* testFile = [[[fileHelper repoPath] stringByAppendingPathComponent: @"test.txt"] absolutePath];
	NSURL* testUrl = [NSURL fileURLWithPath: testFile];
    NSOutputStream* stream = [NSOutputStream outputStreamToMemory];
    [stream open];
    NSError* error = nil;
	bool result = [client cat: [testUrl absoluteString]
                  pegRevision: [SFCRevision head]
                     revision: [SFCRevision head]
                       stream: stream error: &error];
    XCTAssertTrue(result, @"Cat failed %@", error);
    if (result)
    {
        NSData* fileData = [stream propertyForKey: NSStreamDataWrittenToMemoryStreamKey];
        NSString* streamText = [[NSString alloc] initWithData: fileData
                                                     encoding: NSUTF8StringEncoding];
        NSRange rangeOfLine1 = [streamText rangeOfString: @"line 1"];
        XCTAssertTrue(rangeOfLine1.location != NSNotFound,
                      @"File content not found, %@",
                      streamText);
    }
}

-(void) testBlame
{
    NSString* testFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    NSError* error = nil;
    __block int blameCalled = 0;

    bool result = [client blame: testFile
                    pegRevision: [SFCRevision unspecified]
                          start: [SFCRevision number: 0]
                            end: [SFCRevision head]
                    diffOptions: [SFCDiffOptions defaultOptions]
                 ignoreMimeType: false
         includeMergedRevisions: false
                     blameBlock: ^bool(SFCRevNum startRevision,
                                            SFCRevNum endRevision,
                                            int64_t lineNumber,
                                            SFCRevNum revision,
                                            NSDictionary *revProperties,
                                            SFCRevNum mergedRevision,
                                            NSDictionary *mergedRevProperties,
                                            NSString *mergedPath,
                                            NSString *line,
                                            bool localChange)
                                    {
                                        NSLog(@"Blame: %ld %@", (long)lineNumber, line);
                                        blameCalled++;
                                        return true;
                                    }
                          error: &error];
	XCTAssertTrue(result, @"Error in blame %@", error);
	if (result)
    {
        XCTAssertTrue(blameCalled > 0, @"Blame block not called enough");
    }
}

-(void) testList
{
	NSError* error = nil;
    __block int listCalled = 0;
    bool result = [client list: [fileHelper wcPath]
                   pegRevision: nil
                      revision: nil
                         depth: SFC_DEPTH_INFINITY
                    fetchLocks: true
              includeExternals: false
                     listBlock: ^bool(NSString *path,
                                           SFCDirectoryEntry *directoryEntry,
                                           SFCLock *lock,
                                           NSString *absolutePath,
                                           NSURL *externParentURL,
                                           NSString *externTarget)
                            {
                                listCalled++;
                                NSLog(@"List \"%@\", %@", path, directoryEntry);
                                return true;
                            }
                         error: &error];
    XCTAssertTrue(result, @"Error in list %@", error);
	if (result)
    {
        XCTAssertTrue(listCalled > 0, @"Blame block not called enough");
    }

}

-(void) testVersion
{
    SFCVersion* version = [SFCClient version];
    NSLog(@"Version is %@", version);
    XCTAssertTrue([version frameworkMajorVersion] == 1 , @"Incorrect verison %@", version);
    XCTAssertTrue([version svnMajorVersion] == 1 , @"Incorrect verison %@", version);
    XCTAssertTrue([version svnMinorVersion] == 8 , @"Incorrect verison %@", version);
}

-(void) testPropList
{
    NSString* testFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    NSString* newDir = [[fileHelper wcPath] stringByAppendingPathComponent: @"directory"];
    newDir = [newDir absolutePath];

    [fileHelper runTask: SVN_CMD
          withArguments: @[ @"propset", @"svn:global-ignores", @"ignorethis", [fileHelper wcPath]]];
    [fileHelper runTask: SVN_CMD
          withArguments: @[ @"propset", @"myProperty", @"jeremy", testFile ]];
    [fileHelper runTask: SVN_CMD withArguments: @[@"mkdir", newDir]];
    [fileHelper runTask: SVN_CMD
          withArguments: @[ @"propset", @"myProperty", @"foo", newDir ]];

    NSError* error = nil;
    __block int receiverCalled = 0;

    bool result = [client propList: newDir
                       pegRevision: [SFCRevision unspecified]
                          revision: [SFCRevision unspecified]
                             depth: SFC_DEPTH_INFINITY
                       changeLists: nil
                         inherited: true
                         listBlock: ^bool(NSString *path, NSDictionary *properties, NSArray *inheritedProperties)
                                {
                                    NSLog(@"Path = %@", path);
                                    receiverCalled++;
                                    return true;
                                }
                             error: &error];

    if (!result)
    {
        XCTFail(@"prop get failed, reason %@", error);
    }
    else
    {
        XCTAssertTrue(receiverCalled > 0, @"Receiver not called for properties");
    }
}

-(void) testCopy
{
    NSString* testFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    NSString* copyFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"testcopy.txt"];
    NSError* error = nil;
	bool result = [client copySources: @[ [SFCClientCopySource sourceFromPath: testFile pegRevision: [SFCRevision head] revision: [SFCRevision head]] ]
                          destination: copyFile
                              options: 0
                   revisionProperties: nil
                       committedBlock: nil
                                error: &error];
    if (!result)
    {
        XCTFail(@"copy failed, reason %@", error);
    }
    else
    {
        XCTAssertTrue([fileHelper file: copyFile statusIs: @"A"], @"Wrong status for copy");
    }

}

-(void) testDelete
{
    NSString* testFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    NSError* error = nil;
    bool result = [client deletePaths: @[ testFile ]
                                force: false
                            keepLocal: false
                   revisionProperties: nil
                       committedBlock: nil
                                error: &error];
    if (!result)
    {
        XCTFail(@"delete failed, reason %@", error);
    }
    else
    {
        XCTAssertTrue([fileHelper file: testFile statusIs: @"D"], @"Wrong status for delete");
    }
}

-(void) testImport
{
	NSError* error = nil;
    NSURL* importLocation = [[fileHelper repoURI] URLByAppendingPathComponent: @"imported"];
    __block int imported = 0;
    bool result = [client import: [fileHelper wcPath]
                             url: importLocation
                           depth: SFC_DEPTH_INFINITY
                         options: 0
              revisionProperties: nil
                     filterBlock:^bool(NSString *path,
                                       SFCIODirectoryEntry *directoryEntry,
                                       bool *errorOccurred)
                            {
                                NSLog(@"Importing %@", path);
                                imported++;
                                return true;
                            }
                  committedBlock:^bool(SFCCommitInfo *commitInfo)
                            {
                                NSLog(@"Import committed");
                                return true;
                            }
                           error: &error];
    if (!result)
    {
        XCTFail(@"import failed, reason %@", error);
    }
    else
    {
        XCTAssertTrue(imported > 0, @"No files were imported");
    }

}

-(void) testExport
{
    NSError* error = nil;
    NSString* exportPath = [[fileHelper rootPath] stringByAppendingPathComponent: @"export"];
    SFCRevNum resultRevision;
    bool result = [client exportFrom: [[fileHelper repoURI] absoluteString]
                                  to: exportPath
                         pegRevision: [SFCRevision unspecified]
                            revision: [SFCRevision head]
                             options: 0
                               depth: SFC_DEPTH_INFINITY
                           nativeEol: nil
                      resultRevision: &resultRevision
                               error: &error];
    if (!result)
    {
        XCTFail(@"export failed, reason %@", error);
    }
    else
    {
        XCTAssertTrue(resultRevision == 2, @"Invalid result revision %ld", (long) resultRevision);
        NSFileManager* fm = [NSFileManager defaultManager];
        BOOL isDirectory = NO;
        XCTAssertTrue([fm fileExistsAtPath: exportPath isDirectory: &isDirectory] && isDirectory,
                      @"Export path %@does not exist", exportPath);
    }
}

-(void) testMerge
{
    NSString* mainDir = [[fileHelper wcPath] stringByAppendingPathComponent: @"trunk"];
    NSString* mainFile = [mainDir stringByAppendingPathComponent: @"test"];
    NSURL* mainDirInRepo = [[fileHelper repoURI] URLByAppendingPathComponent: @"trunk"];
    NSURL* branchDirInRepo = [[fileHelper repoURI] URLByAppendingPathComponent: @"branch"];
    NSString* branchDirInWc = [[fileHelper wcPath] stringByAppendingPathComponent: @"branch"];
    NSString* branchFileInWc = [branchDirInWc stringByAppendingPathComponent: @"test"];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"mkdir", mainDir]];
    [fileHelper touch: mainFile];
    [fileHelper append: @"line1\n" toFile: mainFile];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"add", mainFile]];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"commit", @"-m", @"Committed trunk", [fileHelper wcPath]]];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"copy", @"-m", @"Made branch", [mainDirInRepo absoluteString], [branchDirInRepo absoluteString]]];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"update", [fileHelper wcPath]]];
    [fileHelper append: @"line2\n" toFile: branchFileInWc];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"commit", @"-m", @"Committed branch", [fileHelper wcPath]]];

    NSError* error = nil;
    bool result = [client mergeSource1: [branchDirInRepo absoluteString]
                             revision1: [SFCRevision number: 4]
                               source2: [branchDirInRepo absoluteString]
                             revision2: [SFCRevision head]
                          targetWcPath: mainDir
                                 depth: SFC_DEPTH_INFINITY
                               options: 0
                          mergeOptions: nil
                                 error: &error];
    if (!result)
    {
        XCTFail(@"export failed, reason %@", error);
    }
    else
    {
        XCTAssertTrue([fileHelper file: mainFile statusIs: @"M"],
                      @"Incorrect status of file");
    }
}

-(void) testMove
{
    NSString* mainDir = [[fileHelper wcPath] stringByAppendingPathComponent: @"trunk"];
    NSString* mainFile = [mainDir stringByAppendingPathComponent: @"test"];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"mkdir", mainDir]];
    [fileHelper touch: mainFile];
    [fileHelper append: @"line1\n" toFile: mainFile];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"add", mainFile]];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"commit", @"-m", @"Committed trunk", [fileHelper wcPath]]];
    NSURL* mainDirInRepo = [[fileHelper repoURI] URLByAppendingPathComponent: @"trunk"];
    NSURL* branchDirInRepo = [[fileHelper repoURI] URLByAppendingPathComponent: @"branch"];
	NSError* error = nil;
    bool result = [client moveSources: @[ [mainDirInRepo absoluteString] ]
                          destination: [branchDirInRepo absoluteString]
                              options: 0
                   revisionProperties: nil
                       committedBlock: nil
                                error: &error];
    if (!result)
    {
        XCTFail(@"move failed, reason %@", error);
    }
    else
    {
        [fileHelper runTask: SVN_CMD withArguments: @[ @"update", [fileHelper wcPath]]];
        NSString* branchDirInWc = [[fileHelper wcPath] stringByAppendingPathComponent: @"branch"];
		NSFileManager* fm = [NSFileManager defaultManager];
        XCTAssertTrue([fm fileExistsAtPath: branchDirInWc], @"Branch dir does not exist.");
    }
}

-(void) testPatch
{
    [self createPatchFile];
	NSError* error = nil;
    bool result = [client patchFile: [PATCH_FILE absolutePath]
                        workingCopy: [[fileHelper wcPath] absolutePath]
                            options: 0
                         stripCount: 0
                         patchBlock:^bool(NSString *canonicalPathFromPatchFile, NSString *absolutePath, NSString *rejectAbsolutePath, bool *errorOccurred) {
                             NSLog(@"Patching %@", canonicalPathFromPatchFile);
                             return true;
                         }
                              error: &error];
    if (!result)
    {
        XCTFail(@"patch failed, reason %@", error);
    }
    else
    {
        NSString* testFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
        XCTAssertTrue([fileHelper file: testFile statusIs: @"M"], @"Test file not patched");
    }
}

-(void) testRelocate
{
	NSError* error = nil;
    NSFileManager* fm = [NSFileManager defaultManager];
    NSString* newRepo = [[fileHelper rootPath] stringByAppendingPathComponent: @"repo2"];
    NSURL* newRepoURI = [NSURL fileURLWithPath: [newRepo absolutePath]];
    if (![fm moveItemAtPath: [fileHelper repoPath] toPath: newRepo error: &error])
    {
        @throw [NSException exceptionWithName: @"testRelocate"
                                       reason: [error localizedDescription]
                                     userInfo: nil];
    }
    bool result = [client relocate: [fileHelper wcPath]
                              from: [fileHelper repoURI]
                                to: newRepoURI
                   ignoreExternals: false
                             error: &error];
    if (!result)
    {
        XCTFail(@"relocate failed, reason %@", error);
    }
    else
    {
        NSString* info = [fileHelper outputOfTask: SVN_CMD withArguments:@[ @"info"]];
        NSRange repo2Range = [info rangeOfString: @"repo2"];
        XCTAssertTrue(repo2Range.location != NSNotFound,
                      @"Relocate seems not to have worked %@", info);
    }
}

-(void) testResolve
{
    NSString* conflictFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
	[fileHelper updateFileInRepository: @"test.txt" appendContent: @"line 3\n"];
    [fileHelper append: @"line 4" toFile: conflictFile];
    [fileHelper runTask: SVN_CMD withArguments: @[@"update", @"--accept", @"postpone", [fileHelper wcPath]]];


	NSError* error = nil;
    bool result = [client resolve: [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"]
                            depth: SFC_DEPTH_INFINITY
                   conflictChoice: SFC_CONFLICT_CHOICE_MINE_FULL
                            error: &error];
    if (!result)
    {
        XCTFail(@"Resolve failed, reason %@", error);
    }
    else
    {
        XCTAssertTrue([fileHelper file: conflictFile statusIs: @"M"],
                      @"Status of %@ is wrong",
                      conflictFile);
    }
}


/*
 *  Do a resolve using a block.  
 *
 *  We introduce a conflict in the file test.txt.  The block resolves it by
 *  selecting the working copy file.
 *
 *  As we have an assertion to test the right path is conflicted in the block,
 *  we turn off retain cycle checking.  The leak isn't anything to worry about.
 */
#pragma clang diagnostic ignored "-Warc-retain-cycles"

-(void) testResolveWithBlock
{
    NSString* conflictFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
	[fileHelper updateFileInRepository: @"test.txt" appendContent: @"line 3\n"];
    [fileHelper append: @"line 4" toFile: conflictFile];
    [fileHelper runTask: SVN_CMD withArguments: @[@"update", @"--accept", @"postpone", [fileHelper wcPath]]];

    [client setConflictResolver: ^SFCConflictResult*(SFCConflictDescription* conflictDesc)
    {
        NSLog(@"Called conflict resolver for %@", conflictDesc);
        XCTAssertTrue([[[conflictDesc localAbsPath] absolutePath] isEqualToString: [conflictFile absolutePath]],
                      @"Wrong node name \n%@ (actual)\n%@ (expected)",
                      [[conflictDesc localAbsPath] absolutePath],
                      [conflictFile absolutePath]);
		return [[SFCConflictResult alloc] initWithChoice: SFC_CONFLICT_CHOICE_MINE_FULL
                                              mergedFile: nil
                                              saveMerged: false];
    }];

	NSError* error = nil;
    bool result = [client resolve: conflictFile
                            depth: SFC_DEPTH_INFINITY
                   conflictChoice: SFC_CONFLICT_CHOICE_UNSPECIFIED
                            error: &error];
    if (!result)
    {
        XCTFail(@"Resolve failed, reason %@", error);
    }
    else
    {
        XCTAssertTrue([fileHelper file: conflictFile statusIs: @"M"],
                      @"Status of %@ is wrong",
                      conflictFile);
    }
}

-(void) testRemoveFromChangeList
{
    NSString* wcFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"changelist", @"myChangeList", wcFile]];
	NSError* error = nil;
    bool result = [client removePaths: @[ wcFile ]
                                depth: SFC_DEPTH_INFINITY
                      fromChangeLists: @[ @"myChangeList" ]
                                error: &error];
    if (!result)
    {
        XCTFail(@"Remove from chngelist failed, reason %@", error);
    }
    else
    {
        NSString* output = [fileHelper outputOfTask: SVN_CMD
                                      withArguments: @[@"status", [fileHelper wcPath]]];
        NSRange range = [output rangeOfString: @"test.txt"];
        XCTAssertTrue(range.location == NSNotFound, @"Test file is still in change list\n%@", output);
    }

}


-(void) testRevPropGet
{
    NSString* wcFile = [[fileHelper wcPath] stringByAppendingPathComponent: @"test.txt"];
    [fileHelper runTask: SVN_CMD withArguments: @[ @"changelist", @"myChangeList", wcFile]];
	NSError* error = nil;
    bool result = false;
    if (!result)
    {
        XCTFail(@"Revprop get failed, reason %@", error);
    }
    else
    {
        XCTFail(@"No success tests");
    }

}


UNIMPLEMENTED_TEST(testRevPropList)
UNIMPLEMENTED_TEST(testRevPropSet)
UNIMPLEMENTED_TEST(testSwitch)
UNIMPLEMENTED_TEST(testIntegration)


@end
